Skip to content

一、概述

Jenkins,原名Hudson, 2011 年改为现在的名字,它 是一个开源的实现持续集成的软件工具。官方网站:http://jenkins-ci.org/。 Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和提醒功能,还能用图表的形式形象地展示项目构建的趋势和稳定性。 特点:

  • 易安装:仅仅一个 java - jar jenkins.war,从官网下载该文件后,直接运行,无需额外的安装,更无需安装数据库;
  • 易配置:提供友好的GUI配置界面; 变更支持:Jenkins能从代码仓库(Subversion/CVS)中获取并产生代码更新列表并输出到编译输出信息中;
  • 支持永久链接:用户是通过web来访问Jenkins的,而这些web页面的链接地址都是永久链接地址,因此,你可以在各种文档中直接使用该链接; 集成E-Mail/RSS/IM:当完成一次集成时,可通过这些工具实时告诉你集成结果(据我所知,构建一次集成需要花费一定时间,有了这个功能,你就可以在等待结果过程中,干别的事情);
  • JUnit/TestNG测试报告:也就是用以图表等形式提供详细的测试报表功能;
  • 支持分布式构建:Jenkins可以把集成构建等工作分发到多台计算机中完成;
  • 文件指纹信息:Jenkins会保存哪次集成构建产生了哪些jars文件,哪一次集成构建使用了哪个版本的jars文件等构建记录;
  • 支持第三方插件:使得 Jenkins 变得越来越强大

1.1 持续集成流程说明

1)首先,开发人员每天进行代码提交,提交到Git仓库

2)然后,Jenkins作为持续集成工具,使用Git工具到Git仓库拉取代码到集成服务器,再配合JDK,Maven等软件完成代码编译,代码测试与审查,测试,打包等工作,在这个过程中每一步出错,都重新再执行一次整个流程。

3)最后,Jenkins把生成的jar或war包分发到测试服务器或者生产服务器,测试人员或用户就可以访问应用。

1.2 相关概念

软件开发生命周期:软件开发生命周期又叫做SDLC(Software Development Life Cycle),它是集合了计划、开发、测试和部署过程的集合

敏捷开发:敏捷开发(Agile Development) 的核心是迭代开发(Iterative Development) 与 增量开发(Incremental Development) 。

持续集成:持续集成( Continuous integration , 简称 CI )指的是,频繁地(一天多次)将代码集成到主干。**持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。**它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

二、页面操作

2.1 插件安装

以安装maven插件为例,演示插件的安装

( 1 )点击左侧的“系统管理”菜单 ,然后点击 管理插件

( 2 )选择“可选插件”选项卡,搜索maven,在列表中选择Maven Integration ,点击“直接安装”按钮

2.2 全局工具配置

  1. 在服务其中安装maven,配置本地仓库地址
  2. jenkin选择jdk配置,设置javahome路径 git配置 maven配置

2.3 用户权限管理

使用Role-based Authorization Strategy 插件来管理Jenkins用户权限

开启权限全局安全配置

Manage Jenkins -> Configure Global Security -> 授权策略切换为"Role-Based Strategy",保存

创建角色

在系统管理页面进入 Manage and Assign Roles -> 点击"Manage Roles"

Global roles(全局角色):管理员等高级用户可以创建基于全局的角色 Project roles(项目角色):针对某个或者某些项目的角色 Slave roles(奴隶角色):节点相关的权限

创建用户

在系统管理页面进入 Manage Users -》 新建用户

给用户分配角色

系统管理页面进入Manage and Assign Roles,点击Assign Roles

2.4 凭证管理

凭据可以用来存储需要密文保护的数据库密码、Gitlab密码信息、Docker私有仓库密码等,以便Jenkins可以和这些第三方的应用进行交互。

需要安装Credentials Binding插件,安装后,左侧会添加凭据菜单。

可以添加的凭证有5种:

Username with password:用户名和密码

SSH Username with private key:

使用SSH用户和密钥

  • Secret fifile:需要保密的文本文件,使用时Jenkins会将文件复制到一个临时目录中,再将文件路径
  • 设置到一个变量中,等构建结束后,所复制的Secret fifile就会被删除。
  • Secret text:需要保存的一个加密的文本串,如钉钉机器人或Github的api token
  • Certifificate:通过上传证书文件的方式

常用的凭证类型有:Username with password(用户密码)和SSH Username with private key(SSH密钥)

创建凭证

  • 用户名密码

Jenkins->凭证->系统->全局凭证->添加凭证-》选择"Username with password",输入Gitlab的用户名和密码,点击"确定"。 即可获取Gitlab中的项目信息。

  • SSH密钥类型

使用root用户生成公钥和私钥 ssh-keygen -t rsa id_rsa:私钥文件 id_rsa.pub:公钥文件

把生成的公钥放在Gitlab中 以root账户登录->点击头像->Settings->SSH Keys -》复制刚才id_rsa.pub文件的内容到这里,点击"Add Key"

在Jenkins中添加凭证,在Jenkins添加一个新的凭证,类型为"SSH Username with private key",把刚才生成私有文件内容复制过来

即可获取Gitlab中的项目信息

2.5 Maven配置

  • 配置Maven的环境变量

    sh
    # vi /etc/profile
    export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk 
    export MAVEN_HOME=/opt/maven 
    export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
    #  配置生效
    source /etc/profile
  • 全局工具配置关联JDK和Maven

    Jenkins->Global Tool Confifiguration->JDK->新增JDK-》添加jdk的home路径

    Jenkins->Global Tool Confifiguration->Maven->新增Maven-》添加maven的路径

  • 修改Maven的settings.xml

    修改本地仓库地址和远程仓库。

  • 验证

    构建->增加构建步骤->Execute Shell -》 输入 mvn clean package

添加Jenkins全局变量

Manage Jenkins->Confifigure System->Global Properties

2.6 Tomcat安装和配置

  • 配置Tomcat用户角色权限

    默认情况下Tomcat是没有配置用户角色权限的,但是,后续Jenkins部署项目到Tomcat服务器,需要用到Tomcat的用户

    sh
    # vi /tomcat/conf/tomcat-users.xml
    <tomcat-users>
        <role rolename="tomcat"/>
        <role rolename="role1"/>
        <role rolename="manager-script"/>
        <role rolename="manager-gui"/> 
        <role rolename="manager-status"/>
        <role rolename="admin-gui"/> 
        <role rolename="admin-script"/>
        <user username="tomcat" password="tomcat" roles="manager-gui,manager- script,tomcat,admin-gui,admin-script"/> 
    </tomcat-users>
    
    # vi /opt/tomcat/webapps/manager/META-INF/context.xml
    # 注释以下内容
    <!-- <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->

三、项目构建

3.1 常用构建触发器

Jenkins内置4种构建触发器:

触发远程构建

其他工程构建后触发(Build after other projects are build)

定时构建(Build periodically)

轮询SCM(Poll SCM)

触发远程构建

配置token值,当请求构建url:http://ip:端口/job/项目名/build?token=token值会进行项目构建

其他工程构建后触发

1)创建pre_job流水线工程 -》 2)配置需要触发的工程 -》 选择构建时的方式

定时构建

build periodically中配置 分 时 日 月 周

轮询SCM

轮询SCM,是指定时扫描本地代码仓库的代码是否有变更,如果代码有变更就触发项目构建。

poll scm中配置定时扫描时间。

3.2 Git hook自动触发构建

利用Gitlab的webhook实现代码push到仓库,立即触发项目自动构建。

1)安装Gitlab Hook插件

2)Jenkins设置自动构建 把生成的webhook URL配置到Gitlab中。

3)Gitlab配置webhook

  • 开启webhook功能

    使用root账户登录到后台,点击Admin Area -> Settings -> Network勾选"Allow requests to the local network from web hooks and services"

  • 在项目添加webhook

    点击项目->Settings->Integrations 填写url 和pushevent

    Manage Jenkins->Confifigure System关闭Enable authen...

Jenkins的参数化构建

  • 在Jenkins添加选择类型参数
  • 在pipeline中 通过${参数名}引用参数

配置邮箱服务器发送构建结果

1)安装Email Extension插件

2)Jenkins设置邮箱相关参数 Manage Jenkins->Configure System 配置邮件通知的相关信息,可发送测试邮件看配置是否存在问题。

3)准备邮件内容,html格式,将文件上传到gitlab,

4) 编写Jenkinsfile添加构建后发送邮件

json
pipeline { 
    agent any stages { 
    stage('拉取代码') { 
        steps { checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url: 'git@192.168.66.100:itheima_group/web_demo.git']]])
} 
}
stage('编译构建') {
    steps { sh label: '', script: 'mvn clean package' } }

stage('项目部署') { 
    steps {deploy adapters: [tomcat8(credentialsId: 'afc43e5e-4a4e-4de6-984f- b1d5a254e434', path: '', url: 'http://192.168.66.102:8080')], contextPath: null, war: 'target/*.war' } } }

post { 
    always { 
    emailext( subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!', body: '${FILE,path="email.html"}', to: 'xxx@qq.com' ) } } }

PS:邮件相关全局参数参考列表:系统设置->Extended E-mail Notification->Content Token Reference,点击旁边的?号

四、代码审查SonarQube

SonarQube是一个用于管理代码质量的开放平台,可以快速的定位代码中潜在的或者明显的错误。目前支持java,C#,C/C++,Python,PL/SQL,Cobol,JavaScrip,Groovy等二十几种编程语言的代码质量管理与检测。 官网:https://www.sonarqube.org/

4.1 安装SonarQube

下载sonar压缩包

安装:

sh
yum install unzip
# 解压


<NolebasePageProperties />




unzip sonarqube-6.7.4.zip 
#  创建目录
mkdir /opt/sonar
#  移动文件
mv sonarqube-6.7.4/* /opt/sonar
#  创建sonar用户,必须sonar用于启动,否则报错
useradd sonar
# 更改sonar目录及文件权限
chown -R sonar. /opt/sonar

修改sonar配置文件

properties
# vi /opt/sonarqube-6.7.4/conf/sonar.properties
sonar.jdbc.username=root sonar.jdbc.password=Root@123
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=
maxPerformance&useSSL=false

注意:sonar默认监听9000端口,如果9000端口被占用,需要更改

启动sonar

sh
cd /opt/sonarqube-6.7.4
#  启动
su sonar ./bin/linux-x86-64/sonar.sh start
#  查看状态
su sonar ./bin/linux-x86-64/sonar.sh status
# 停止
su sonar ./bin/linux-x86-64/sonar.sh stop 
# 查看日志
tail -f logs/sonar.logs

访问sonar http://ip:9000 默认账户:admin/admin

4.2 与jenkins集成

1)创建sonar的token

2)安装SonarQube Scanner插件

3)添加SonarQube凭证 选择Secret text类型

4)Jenkins进行SonarQube配置 Manage Jenkins->Confifigure System->SonarQube servers 配置名称和地址

5)Manage Jenkins->Global Tool Configuration,选择自动安装及版本呢

6)SonaQube关闭审查结果上传到SCM功能 SCM-》Administration->打开Disable the scm sensor

7)在项目添加SonaQube代码审查

  • 非流水线项目

    properties
    # must be unique in a given SonarQube instance 
    sonar.projectKey=web_demo 
    # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. 
    sonar.projectName=web_demo 
    sonar.projectVersion=1.0 
    # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 
    # This property is optional if sonar.modules is set.
    sonar.sources=. 
    sonar.exclusions=**/test/**,**/target/** 
    sonar.java.source=1.8
    sonar.java.target=1.8 
    # Encoding of the source code. 
    Default is default system encoding 
    sonar.sourceEncoding=UTF-8
  • 在项目添加SonaQube代码审查(流水线项目)

    项目根目录下,创建sonar-project.properties文件

    properties
    # must be unique in a given SonarQube instance
    sonar.projectKey=web_demo_pipline
    # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
    sonar.projectName=web_demo_pipline
    sonar.projectVersion=1.0
    
    # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
    # This property is optional if sonar.modules is set.
    sonar.sources=.
    sonar.exclusions=**/test/**,**/target/**
    
    sonar.java.source=1.8
    sonar.java.target=1.8
    
    # Encoding of the source code. Default is default system encoding
    sonar.sourceEncoding=UTF-8

    修改Jenkinsfifile,加入SonarQube代码审查阶段

    json
    pipeline { 
    agent any stages { 
    stage('拉取代码') { 
        steps { checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url: 'git@192.168.66.100:itheima_group/web_demo.git']]]) } }
    
    	stage('编译构建') {
            steps { sh label: '', script: 'mvn clean package' } }
    
    	stage('SonarQube代码审查') {
            steps{ script { scannerHome = tool 'sonarqube-scanner' }
    
    	withSonarQubeEnv('sonarqube6.7.4') { sh "${scannerHome}/bin/sonar-scanner" } } }
    
    	stage('项目部署') { 
            steps { deploy adapters: [tomcat8(credentialsId: 'afc43e5e-4a4e-4de6-984f- b1d5a254e434', path: '', url: 'http://192.168.66.102:8080')], contextPath: null, war: 'target/*.war' } } }
    
    	post { 
            always {
            emailext( subject: '构建通知:${PROJECT_NAME} - Build # ${BUILD_NUMBER} - ${BUILD_STATUS}!', body: '${FILE,path="email.html"}', to: '1014671449@qq.com' ) } } }

    8)到SonarQube的UI界面查看审查结果

五、构建maven项目

Jenkins中自动构建项目的类型有很多,常用的有以下三种:

自由风格软件项目(FreeStyle Project)

Maven项目(Maven Project)

流水线项目(Pipeline Project)

推荐使用流水线类型,因为灵活度非常高

5.1 自由风格软件项目

1)创建项目

2)配置源码管理,从gitlab拉取代码

3)编译打包 构建->添加构建步骤->Executor Shell

sh
echo "开始编译和打包" 
mvn clean package
echo "编译和打包结束"

4)部署 把项目部署到远程的Tomcat里面

安装 Deploy to container插件 -》 添加Tomcat用户凭证 -》 添加构建后操作 -》 选择 Deploy war...

5.2 Maven项目构建

1)安装Maven Integration插件

2)创建Maven项目

3)配置项目 bulid中配置pom.xml的路径 及maven的打包命令 clean package mvn前缀不需要添加

5.3 Pipeline流水线项目构建

Pipeline,简单来说,就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排和可视化的工作。

Pipeline 脚本是由 Groovy 语言实现的,但是我们没必要单独去学习 GroovyPipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法Pipeline 也有两种创建方法:可以直接在 Jenkins 的 Web UI 界面中输入脚本;也可以通过创建一个 Jenkinsfifile 脚本文件放入项目源码库中(一般我们都推荐在 Jenkins 中直接从源代码控制(SCM)中直接载入 Jenkinsfifile Pipeline 这种方法)。

安装Pipeline插件

Manage Jenkins->Manage Plugins->可选插件 -》 Pipeline

Declarative声明式-Pipeline

创建项目-》 流水线->选择HelloWorld模板

生成内容如下:

json
pipeline { 
    agent any 
    stages { 
        stage('拉取代码') {
            steps { echo '拉取代码' } 
        }

        stage('编译构建') {
            steps { echo '编译构建' }
        }

        stage('项目部署') { 
            steps { echo '项目部署' }
        } 
	}
}

stages:代表整个流水线的所有执行阶段。通常stages只有1个,里面包含多个stage

stage:代表流水线中的某个阶段,可能出现n个。一般分为拉取代码,编译构建,部署等阶段。

steps:代表一个阶段内需要执行的逻辑。steps里面是shell脚本,git拉取代码,ssh远程发布等任意内容。

Scripted Pipeline脚本式

创建项目 - > 流水线 -》 选择"Scripted Pipeline"

json
pipeline {
    agent any stages {
        stage('拉取代码') { 
                steps {
                    checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '68f2087f-a034-4d39-a9ff-1f776dd3dfa8', url: 'git@192.168.66.100:itheima_group/web_demo.git']]])
                } 
        }
        stage('编译构建') { 
                steps { sh label: '', script: 'mvn clean package' }
        }
        stage('项目部署') { 
                steps { 
                    deploy adapters: [tomcat8(credentialsId: 'afc43e5e-4a4e-4de6-984f- b1d5a254e434', path: '', url: 'http://192.168.66.102:8080')], contextPath: null, war: 'target/*.war' 
                }
        }
	}
}

Node:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行环境,后续讲到Jenkins的Master-Slave架构的时候用到。

Stage:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如:Build、Test、Deploy,Stage 是一个逻辑分组的概念。

Step:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh ‘make’,就相当于我们平时 shell 终端中执行 make 命令一样。

Pipeline Script from SCM

1)在项目根目录建立Jenkinsfifile文件,把pipeline脚本存在文件中

2) 构建时流水线选择Pipeline Script from SCM,然后选择pipeline脚本的路径。

六、Jenkins+Docker+SpringCloud微服务持续集成

6.1 持续集成流程

1)开发人员每天把代码提交到Gitlab代码仓库

2)Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到

Harbor私有仓库。

3)Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。

4)最后,用户可以访问到容器

6.2 使用Dockerfile编译、生成镜像

利用dockerfile-maven-plugin插件构建Docker镜像

1)在每个微服务项目的pom.xml加入dockerfile-maven-plugin插件

xml
<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>dockerfile-maven-plugin</artifactId> 
    <version>1.3.6</version> 
    <configuration> 
        <repository>${project.artifactId}</repository> 
        <buildArgs> 
            <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
        </buildArgs> 
    </configuration> 
</plugin>

2)在每个微服务项目根目录下建立Dockerfile文件

dockerfile
#FROM java:8
FROM openjdk:8-jdk-alpine 
ARG JAR_FILE 
COPY ${JAR_FILE} app.jar 
EXPOSE 10086
ENTRYPOINT ["java","-jar","/app.jar"]

3)修改Jenkinsfile构建脚本 通过docker构建并上传到Harbor

需要再jenkinx中创建harbor的凭证

json
//gitlab的凭证 
def git_auth = "68f2087f-a034-4d39-a9ff-1f776dd3dfa8" 
//构建版本的名称 
def tag = "latest" 
//Harbor私服地址 
def harbor_url = "192.168.66.102:85/tensquare/" 

node { 
    stage('拉取代码') { 
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: 'git@192.168.66.100:itheima_group/tensquare_back.git']]]) }
    stage('代码审查') {
        def scannerHome = tool 'sonarqube-scanner' 
        withSonarQubeEnv('sonarqube6.7.4') { 
            sh """ cd ${project_name} ${scannerHome}/bin/sonar-scanner """ } }

	stage('编译,构建镜像') {
        //定义镜像名称 
        def imageName = "${project_name}:${tag}" 
        //编译,安装公共工程 
        sh "mvn -f tensquare_common clean install" 
        //编译,构建本地镜像 
        sh "mvn -f ${project_name} clean package dockerfile:build" 
        //给镜像打标签 
        sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"
        //登录Harbor,并上传镜像 
        withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 
            //登录 
            sh "docker login -u ${username} -p ${password} ${harbor_url}" 
            //上传镜像 
            sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" }
			//删除本地镜像 
			sh "docker rmi -f ${imageName}"
			sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" }

6.3 拉取镜像和发布应用

安装 Publish Over SSH 插件,可以实现远程发送Shell命令

1)拷贝公钥到远程服务器

2)系统配置->添加远程服务器

3)修改Jenkinsfile构建脚本

json
//gitlab的凭证 
def git_auth = "68f2087f-a034-4d39-a9ff-1f776dd3dfa8" 
//构建版本的名称 
def tag = "latest"
//Harbor私服地址 
def harbor_url = "192.168.66.102:85" 
//Harbor的项目名称 
def harbor_project_name = "tensquare" 
//Harbor的凭证 
def harbor_auth = "ef499f29-f138-44dd-975e-ff1ca1d8c933" 

node { 
    stage('拉取代码') { 
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: 'git@192.168.66.100:itheima_group/tensquare_back.git']]]) }
    
    stage('代码审查') {
        def scannerHome = tool 'sonarqube-scanner' 
        withSonarQubeEnv('sonarqube6.7.4') {
            sh """ cd ${project_name} ${scannerHome}/bin/sonar-scanner """ } }

	stage('编译,构建镜像,部署服务') { 
        //定义镜像名称
        def imageName = "${project_name}:${tag}"
        //编译并安装公共工程 
        sh "mvn -f tensquare_common clean install"
        //编译,构建本地镜像
        sh "mvn -f ${project_name} clean package dockerfile:build" 
        //给镜像打标签 
        sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"
        //登录Harbor,并上传镜像 
        withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 
            //登录 
            sh "docker login -u ${username} -p ${password} ${harbor_url}"
            //上传镜像
            sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" }
			//删除本地镜像
			sh "docker rmi -f ${imageName}" 
			sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
			sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project_name $project_name $tag $port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) } }

编写deploy.sh部署脚本,放到/opt/jenkins_shell目录下,且文件至少有执行权限

sh
#! /bin/sh
#接收外部参数
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5

imageName=$harbor_url/$harbor_project_name/$project_name:$tag

echo "$imageName"

#查询容器是否存在,存在则删除
containerId=`docker ps -a | grep -w ${project_name}:${tag}  | awk '{print $1}'`
if [ "$containerId" !=  "" ] ; then
    #停掉容器
    docker stop $containerId

    #删除容器
    docker rm $containerId
	
	echo "成功删除容器"
fi

#查询镜像是否存在,存在则删除
imageId=`docker images | grep -w $project_name  | awk '{print $3}'`

if [ "$imageId" !=  "" ] ; then
      
    #删除镜像
    docker rmi -f $imageId
	
	echo "成功删除镜像"
fi

# 登录Harbor
docker login -u eric -p Eric123456 $harbor_url

# 下载镜像
docker pull $imageName

# 启动容器
docker run -di -p $port:$port $imageName

echo "容器启动成功"

6.4 部署前端静态web网站

  • 前提:安装nginx、关闭se-linux

  • 安装NodeJS插件 Manage Jenkins->Global Tool Configuration

  • 创建前端流水线项目

  • 建立Jenkinsfile构建脚本

    json
    //gitlab的凭证 
    def git_auth = "68f2087f-a034-4d39-a9ff-1f776dd3dfa8" 
    
    node { 
        stage('拉取代码') {
            checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: 'git@192.168.66.100:itheima_group/tensquare_front.git']]]) }
        
        stage('打包,部署网站') { 
            //使用NodeJS的npm进行打包 
            nodejs('nodejs12'){ sh '''npm install npm run build ''' }
            
            //=====以下为远程调用进行项目部署======== 
            sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/usr/share/nginx/html', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) } }

6.5 同时构建多个项目并部署到多个服务器

设计Jenkins集群项目的构建参数

1)安装Extended Choice Parameter插件

2)创建流水线项目

3)添加参数 添加分支名称 和项目多选框 ,多选框可配置描述和默认选项

tensquare_eureka_server@10086,tensquare_zuul@10020,tensquare_admin_service@9001, tensquare_gathering@9002

4)完成微服务构建镜像,上传私服

json
//gitlab的凭证 
def git_auth = "68f2087f-a034-4d39-a9ff-1f776dd3dfa8"
//构建版本的名称 
def tag = "latest" 
//Harbor私服地址 
def harbor_url = "192.168.66.102:85" 
//Harbor的项目名称 
def harbor_project_name = "tensquare"
//Harbor的凭证 
def harbor_auth = "ef499f29-f138-44dd-975e-ff1ca1d8c933" 

node { 
    //把选择的项目信息转为数组 
    def selectedProjects = "${project_name}".split(',') 

	stage('拉取代码') { 
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${git_auth}', url: 'git@192.168.66.100:itheima_group/tensquare_back_cluster.git']]]) }

	stage('代码审查') { 
        def scannerHome = tool 'sonarqube-scanner' 
        withSonarQubeEnv('sonarqube6.7.4') { 
            for(int i=0;i<selectedProjects.size();i++){
                //取出每个项目的名称和端口 
                def currentProject = selectedProjects[i];
				//项目名称 
				def currentProjectName = currentProject.split('@')[0] 
				//项目启动端口
				def currentProjectPort = currentProject.split('@')[1] 
				sh """cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """ 
				echo "${currentProjectName}完成代码审查" } } }

	stage('编译,构建镜像,部署服务') {
        //编译并安装公共工程 
        sh "mvn -f tensquare_common clean install" 
        for(int i=0;i<selectedProjects.size();i++){
            //取出每个项目的名称和端口 
            def currentProject = selectedProjects[i]; 
			//项目名称 
			def currentProjectName = currentProject.split('@')[0] 
			//项目启动端口 
			def currentProjectPort = currentProject.split('@')[1] 
			//定义镜像名称 
			def imageName = "${currentProjectName}:${tag}" 
			//编译,构建本地镜像
			sh "mvn -f ${currentProjectName} clean package dockerfile:build"
			//给镜像打标签 
			sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" 
			//登录Harbor,并上传镜像 
			withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 
                //登录 
                sh "docker login -u ${username} -p ${password} ${harbor_url}" 
                //上传镜像 
                sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" }
				//删除本地镜像
				sh "docker rmi -f ${imageName}" 
				sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" 
				//=====以下为远程调用进行项目部署======== 
				//sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project_name $currentProjectName $tag $currentProjectPort", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) 
				echo "${currentProjectName}完成编译,构建镜像" } } }

微服务多服务器远程发布

1)拷贝公钥到远程服务器

2)系统配置->添加远程服务器

3)修改Docker配置信任Harbor私服地址 重启Docker

4) 添加部署服务器多选框

5)修改Jenkinsfile构建脚本

json
//gitlab的凭证 
def git_auth = "68f2087f-a034-4d39-a9ff-1f776dd3dfa8" 
//构建版本的名称 
def tag = "latest" 
//Harbor私服地址 
def harbor_url = "192.168.66.102:85" 
//Harbor的项目名称 
def harbor_project_name = "tensquare" 
//Harbor的凭证 
def harbor_auth = "ef499f29-f138-44dd-975e-ff1ca1d8c933" 

node { 
    //把选择的项目信息转为数组 
    def selectedProjects = "${project_name}".split(',')
	//把选择的服务区信息转为数组 
	def selectedServers = "${publish_server}".split(',') 

	stage('拉取代码') { 
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${git_auth}', url: 'git@192.168.66.100:itheima_group/tensquare_back_cluster.git']]]) }
	stage('代码审查') { 
        def scannerHome = tool 'sonarqube-scanner' 
        withSonarQubeEnv('sonarqube6.7.4') {
            for(int i=0;i<selectedProjects.size();i++){ 
                //取出每个项目的名称和端口 
                def currentProject = selectedProjects[i];
				//项目名称 
				def currentProjectName = currentProject.split('@')[0] 
				//项目启动端口
				def currentProjectPort = currentProject.split('@')[1] 
				sh """cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """ 
				echo "${currentProjectName}完成代码审查" } } }

	stage('编译,构建镜像,部署服务') { 
        //编译并安装公共工程 
        sh "mvn -f tensquare_common clean install"
        for(int i=0;i<selectedProjects.size();i++){ 
            //取出每个项目的名称和端口 
            def currentProject = selectedProjects[i];
			//项目名称 
			def currentProjectName = currentProject.split('@')[0] 
			//项目启动端口 
			def currentProjectPort = currentProject.split('@')[1] 
			//定义镜像名称 
			def imageName = "${currentProjectName}:${tag}"
			//编译,构建本地镜像 
			sh "mvn -f ${currentProjectName} clean package dockerfile:build"
			//给镜像打标签
			sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" 
			//登录Harbor,并上传镜像 
			withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) { 
                //登录
                sh "docker login -u ${username} -p ${password} ${harbor_url}" 
                //上传镜像 
                sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" }
				//删除本地镜像
				sh "docker rmi -f ${imageName}" 
				sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" 
				//=====以下为远程调用进行项目部署======== 
				for(int j=0;j<selectedServers.size();j++){ 
                    //每个服务名称 
                    def currentServer = selectedServers[j] 
					//添加微服务运行时的参数:
					spring.profiles.active def activeProfile = "--spring.profiles.active=" 
					if(currentServer=="master_server"){ 
                        activeProfile = activeProfile+"eureka-server1" 
                    }else if(currentServer=="slave_server1"){ 
                        activeProfile = activeProfile+"eureka-server2"
                    }

					sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project_name $currentProjectName $tag $currentProjectPort $activeProfile", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) }
				echo "${currentProjectName}完成编译,构建镜像" } } }

6)编写deployCluster.sh部署脚本

sh
#! /bin/sh
#接收外部参数
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5
profile=$6

imageName=$harbor_url/$harbor_project_name/$project_name:$tag

echo "$imageName"

#查询容器是否存在,存在则删除
containerId=`docker ps -a | grep -w ${project_name}:${tag}  | awk '{print $1}'`

if [ "$containerId" !=  "" ] ; then
    #停掉容器
    docker stop $containerId

    #删除容器
    docker rm $containerId
	
	echo "成功删除容器"
fi

#查询镜像是否存在,存在则删除
imageId=`docker images | grep -w $project_name  | awk '{print $3}'`

if [ "$imageId" !=  "" ] ; then
      
    #删除镜像
    docker rmi -f $imageId
	
	echo "成功删除镜像"
fi


# 登录Harbor
docker login -u eric -p Eric123456 $harbor_url

# 下载镜像
docker pull $imageName

# 启动容器
docker run -di -p $port:$port $imageName $profile

echo "容器启动成功"

七、基于Kubernetes/K8S构建Jenkins持续集成平台

7.1 Jenkins的Master-Slave分布式构建

Jenkins的Master-Slave分布式构建,就是通过将构建过程分配到从属Slave节点上,从而减轻Master节点的压力,而且可以同时构建多个,有点类似负载均衡的概念。

实现分布式构建

1)开启代理程序的TCP端口 Manage Jenkins -> Configure Global Security -》代理——》 随机选取

2)新建节点 Manage Jenkins—Manage Nodes—新建节点

安装和配置节点

下载agent.jar,并上传到Slave节点,然后执行页面提示的命令:

sh
java -jar agent.jar -jnlpUrl http://192.168.66.101:8888/computer/slave1/slave- agent.jnlp -secret f2ecbb99e0c81331e8b7a7917a94d478f39cb9763fc6c66d9a9741c61f9ae6d6 -workDir "/root/jenkins"

7.2 Kubernetes实现Master-Slave分布式构建方案

NFS安装

NFS(Network File System),它最大的功能就是可以通过网络,让不同的机器、不同的操作系统可以共享彼此的文件。我们可以利用NFS共享Jenkins运行的配置文件、Maven的仓库依赖文件等

1)安装NFS服务(在所有K8S的节点都需要安装)

sh
yum install -y nfs-utils

2)创建共享目录

sh
mkdir -p /opt/nfs/jenkins 
vi /etc/exports 
# 编写NFS的共享配置 内容如下: 
# *代表对所有IP都开放此目录,rw是读写
/opt/nfs/jenkins *(rw,no_root_squash)

3)启动服务

sh
# 开机启动
systemctl enable nfs
#  启动
systemctl start nfs

4)查看NFS共享目录

sh
showmount -e 192.168.66.101

7.3 在Kubernetes安装Jenkins-Master

创建NFS client provisioner

nfs-client-provisioner 是一个Kubernetes的简易NFS的外部provisioner,本身不提供NFS,需要现有的NFS服务器提供存储。

1)上传nfs-client-provisioner构建文件 修改deployment.yaml,使用之前配置NFS服务器和目录

yaml
# class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "true"
yaml
# deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: lizhenliang/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.66.101 
            - name: NFS_PATH
              value: /opt/nfs/jenkins/
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.66.101
            path: /opt/nfs/jenkins/
yaml
# rbac.yaml
kind: ServiceAccount
apiVersion: v1
metadata:
  name: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

2)构建nfs-client-provisioner的pod资源

sh
cd nfs-client 
kubectl create -f .

3)查看pod是否创建成功

安装Jenkins-Master

1)上传Jenkins-Master构建文件

dockerfile
# rbac.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins
  namespace: kube-ops
rules:
  - apiGroups: ["extensions", "apps"]
    resources: ["deployments"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
  - apiGroups: [""]
    resources: ["services"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create","delete","get","list","patch","update","watch"]
  - apiGroups: [""]
    resources: ["pods/exec"]
    verbs: ["create","delete","get","list","patch","update","watch"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get","list","watch"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: jenkins
  namespace: kube-ops
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: kube-ops
    
---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkinsClusterRole
  namespace: kube-ops
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get","list","watch"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: jenkinsClusterRuleBinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkinsClusterRole
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: kube-ops
yaml
# Service.yaml
apiVersion: v1
kind: Service
metadata:
  name: jenkins
  namespace: kube-ops
  labels:
    app: jenkins
spec:
  selector:
    app: jenkins
  type: NodePort
  ports:
  - name: web
    port: 8080
    targetPort: web
  - name: agent
    port: 50000
    targetPort: agent
yaml
# ServiceaAcount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: kube-ops
yaml
# StatefulSet.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: jenkins
  labels:
    name: jenkins
  namespace: kube-ops
spec:
  serviceName: jenkins
  selector:
    matchLabels:
      app: jenkins
  replicas: 1
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      name: jenkins
      labels:
        app: jenkins
    spec:
      terminationGracePeriodSeconds: 10
      serviceAccountName: jenkins
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts-alpine
          imagePullPolicy: IfNotPresent
          ports:
          - containerPort: 8080
            name: web
            protocol: TCP
          - containerPort: 50000
            name: agent
            protocol: TCP
          resources:
            limits:
              cpu: 1
              memory: 1Gi
            requests:
              cpu: 0.5
              memory: 500Mi
          env:
            - name: LIMITS_MEMORY
              valueFrom:
                resourceFieldRef:
                  resource: limits.memory
                  divisor: 1Mi
            - name: JAVA_OPTS
              value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
          livenessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 5
            failureThreshold: 12
          readinessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 5
            failureThreshold: 12
      securityContext:
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: jenkins-home
    spec:
      storageClassName: "managed-nfs-storage"
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

在StatefulSet.yaml文件,声明了利用nfs-client-provisioner进行Jenkins-Master文件存储

Service发布方法采用NodePort,会随机产生节点访问端口

2)创建kube-ops的namespace

sh
kubectl create namespace kube-ops

3)构建Jenkins-Master的pod资源

sh
cd jenkins-master 
kubectl create -f .

4)查看pod是否创建成功

sh
kubectl get pods -n kube-ops

5)查看信息,并访问

sh
kubectl describe pods -n kube-ops

# 查看分配端口
kubectl get service -n kube-ops

7.4 Jenkins与Kubernetes整合

1)安装Kubernetes插件 系统管理->插件管理->可选插件

2)实现Jenkins与Kubernetes整合 系统管理->系统配置->云->新建云->Kubernetes

kubernetes地址采用了kube的服务器发现:https://kubernetes.default.svc.cluster.local

namespace填kube-ops,然后点击Test Connection,如果出现 Connection test successful 的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信 Jenkins URL 地址:http://jenkins.kube-ops.svc.cluster.local:8080

构建Jenkins-Slave自定义镜像

Dockerfile文件内容如下:

dockerfile
FROM jenkins/jnlp-slave:latest

MAINTAINER itcast

# 切换到 root 账户进行操作
USER root

# 安装 maven
COPY apache-maven-3.6.2-bin.tar.gz .

RUN tar -zxf apache-maven-3.6.2-bin.tar.gz && \
    mv apache-maven-3.6.2 /usr/local && \
    rm -f apache-maven-3.6.2-bin.tar.gz && \
    ln -s /usr/local/apache-maven-3.6.2/bin/mvn /usr/bin/mvn && \
    ln -s /usr/local/apache-maven-3.6.2 /usr/local/apache-maven && \
    mkdir -p /usr/local/apache-maven/repo

COPY settings.xml /usr/local/apache-maven/conf/settings.xml

USER jenkins

构建出一个新镜像:jenkins-slave-maven:latest

然把镜像上传到Harbor的公共库library中

sh
docker tag jenkins-slave-maven:latest 192.168.66.102:85/library/jenkins-slave- maven:latest 
docker push 192.168.66.102:85/library/jenkins-slave-maven:latest

测试Jenkins-Slave的流水线项目

json
def git_address = "http://192.168.66.100:82/itheima_group/tensquare_back_cluster.git"
def git_auth = "9d9a2707-eab7-4dc9-b106-e52f329cbc95"

//创建一个Pod的模板,label为jenkins-slave
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
    containerTemplate(
        name: 'jnlp', 
        image: "192.168.66.102:85/library/jenkins-slave-maven:latest"
    )
  ]
) 
{
  //引用jenkins-slave的pod模块来构建Jenkins-Slave的pod
  node("jenkins-slave"){
      // 第一步
      stage('拉取代码'){
         checkout([$class: 'GitSCM', branches: [[name: 'master']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
      }
  }
}

7.5 Jenkins+Kubernetes+Docker完成微服务持续集成

1)创建NFS共享目录 让所有Jenkins-Slave构建指向NFS的Maven的共享仓库目录

sh
vi /etc/exports 
# 添加内容: 
/opt/nfs/jenkins *(rw,no_root_squash)
/opt/nfs/maven *(rw,no_root_squash) 
#  重启NFS
systemctl restart nfs

2)创建项目,编写构建Pipeline

json
def git_address = "http://192.168.66.100:82/itheima_group/tensquare_back_cluster.git"
def git_auth = "9d9a2707-eab7-4dc9-b106-e52f329cbc95"
//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = "192.168.66.102:85"
//Harbor的项目名称
def harbor_project_name = "tensquare"
//Harbor的凭证
def harbor_auth = "71eff071-ec17-4219-bae1-5d0093e3d060"
def secret_name = "registry-auth-secret"
//k8s凭证
def k8s_auth = "c5fe8670-f5a7-4b1a-811c-48ab5de2aed9";


podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
    containerTemplate(
        name: 'jnlp', 
        image: "192.168.66.102:85/library/jenkins-slave-maven:latest"
    ),
    containerTemplate(
        name: 'docker', 
        image: "docker:stable",
        ttyEnabled: true,
        command: 'cat'
    ),
  ],
  volumes: [
    hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
    nfsVolume(mountPath: '/usr/local/apache-maven/repo', serverAddress: '192.168.66.101' , serverPath: '/opt/nfs/maven'),
  ],
) 
{
  node("jenkins-slave"){
      // 第一步
      stage('拉取代码'){
         checkout([$class: 'GitSCM', branches: [[name: '${branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
      }
      // 第二步
      stage('代码编译'){
           //编译并安装公共工程
         sh "mvn -f tensquare_common clean install" 
      }
      // 第三步
      stage('构建镜像,部署项目'){
	        //把选择的项目信息转为数组
			def selectedProjects = "${project_name}".split(',')
			
            for(int i=0;i<selectedProjects.size();i++){
                //取出每个项目的名称和端口
                def currentProject = selectedProjects[i];
                //项目名称
                def currentProjectName = currentProject.split('@')[0]
                //项目启动端口
                def currentProjectPort = currentProject.split('@')[1]

                 //定义镜像名称
                 def imageName = "${currentProjectName}:${tag}"
				 
				 //编译,构建本地镜像
				 sh "mvn -f ${currentProjectName} clean package dockerfile:build"
				 container('docker') {

					 //给镜像打标签
					 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}"

					 //登录Harbor,并上传镜像
					 withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
						  //登录
						  sh "docker login -u ${username} -p ${password} ${harbor_url}"
						  //上传镜像
						  sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}"
					 }

					 //删除本地镜像
					 sh "docker rmi -f ${imageName}"
					 sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
				 }
				 
				 def deploy_image_name = "${harbor_url}/${harbor_project_name}/${imageName}"
				 
				 //部署到K8S
			     sh """
                        sed -i 's#\$IMAGE_NAME#${deploy_image_name}#' ${currentProjectName}/deploy.yml
						sed -i 's#\$SECRET_NAME#${secret_name}#' ${currentProjectName}/deploy.yml
                 """
                      
                 kubernetesDeploy configs: "${currentProjectName}/deploy.yml", kubeconfigId: "${k8s_auth}"
         } 
      }
  }
}

3)建立k8s认证凭证

kubeconfig到k8s的Master节点复制 cat /root/.kube/config

4)生成Docker凭证

Docker凭证,用于Kubernetes到Docker私服拉取镜像

sh
#登录Harbor 
docker login -u itcast -p Itcast123 192.168.66.102:85 
# 生成
kubectl create secret docker-registry registry-auth-secret --docker- server=192.168.66.102:85 --docker-username=itcast --docker-password=Itcast123 -- docker-email=itcast@itcast.cn 
# 查看密钥
kubectl get secret

5)在每个项目下建立deploy.yml

yaml
---
apiVersion: v1
kind: Service
metadata:
  name: eureka
  labels:
    app: eureka
spec:
  type: NodePort
  ports:
    - port: 10086
      name: eureka
      targetPort: 10086
  selector:
    app: eureka
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: eureka
spec:
  serviceName: "eureka"
  replicas: 2
  selector:
    matchLabels:
      app: eureka
  template:
    metadata:
      labels:
        app: eureka
    spec:
      imagePullSecrets:
        - name: $SECRET_NAME
      containers:
        - name: eureka
          image: $IMAGE_NAME
          ports:
            - containerPort: 10086
          env:
            - name: MY_POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: EUREKA_SERVER
              value: "http://eureka-0.eureka:10086/eureka/,http://eureka-1.eureka:10086/eureka/"
            - name: EUREKA_INSTANCE_HOSTNAME
              value: ${MY_POD_NAME}.eureka
  podManagementPolicy: "Parallel"

6)项目构建后,查看服务创建情况

sh
kubectl get pods -owide 
kubectl get service

八、安装

需安装jdk环境

8.1 常规安装

  • 下载 wget https://pkg.jenkins.io/redhat/jenkins‐ 2. 83 ‐ 1. 1 .noarch.rpm

  • 安装 rpm ‐ivh jenkins‐ 2. 83 ‐ 1. 1 .noarch.rpm

  • 配置用户的端口

    properties
    # /etc/sysconfig/jenkins
    JENKINS_USER="root"
    JENKINS_PORT="8888"
  • 启动服务 systemctl start jenkins

  • 访问 ip : 8888 从 /var/lib/jenkins/secrets/initialAdminPassword中获取初始密码串

  • 跳过插件安装(Jenkins插件需要连接默认官网下载,速度非常慢)

  • 创建管理员账户。

  • 修改Jenkins插件下载地址

    Jenkins->Manage Jenkins->Manage Plugins,点击Available

    sh
    # cd /var/lib/jenkins/updates
    sed -i 's/http:\/\/updates.jenkinsci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json && sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json

    Manage Plugins点击Advanced,把Update Site改为国内插件下载地址

    https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

    Sumbit后,在浏览器输入:

    http://192.168.66.101:8888/restart ,重启Jenkins。

8.2 插件安装

汉化插件

Jenkins->Manage Jenkins->Manage Plugins,点击Available,搜索"Chinese"